在上一篇文章中,我們介紹了 Rust 中高效能的 Web 框架 Actix。本篇文章,我們將探討 Rust 的另一個知名 Web 框架:Rocket,並對比 Actix 與 Rocket 在實際開發中的應用差異,幫助讀者更好地理解這兩個 Web 框架。
Rocket 是一個專為 Rust 語言設計的 Web 框架,它以簡單易用、有方便的錯誤處理機制與靜態類型檢查機制。Rocket 是採用了同步編程模式,並透過巨集與強類型系統進行靜態檢查,這讓我們在編譯時就能檢測大多數錯誤,而非等到運行時才發現問題。這種靜態檢查對於增強程式的健壯性有極大幫助,尤其是在需要高度可靠的應用場景中。
我們首先來看看如何使用 Rocket 建立一個簡單的 Web 應用程式:
使用以下命令建立新專案並安裝 Rocket 套件:
cargo new rocket-web
cd rocket-web
然後在 Cargo.toml
文件中添加 Rocket 套件:
[dependencies]
rocket = "0.5.0-rc.1"
接下來執行 cargo build
,來下載並編譯 Rocket 框架。
我們可以編寫一個簡單的 main.rs
,來建立一個基本的 HTTP 伺服器,回傳 "Hello from Rocket!" 給用戶:
// 使用 #[macro_use] 將 Rocket 巨集引入到程式中,使我們可以使用路由相關的巨集
#[macro_use] extern crate rocket;
// 定義一個路由,處理 GET 請求,當使用者訪問 "/" 路徑時,會觸發這個函數
#[get("/")]
// 定義一個名為 index 的函數,它會回傳一個靜態字串
fn index() -> &'static str {
// 當使用者訪問 "/" 路徑時,回傳這個字串作為 HTTP 回應
"Hello from Rocket!"
}
// 使用 #[launch] 巨集,這標記該函數為 Rocket 應用的啟動點
#[launch]
// 定義一個名為 rocket 的函數,回傳 Rocket 伺服器的設定
fn rocket() -> _ {
// 建立並設定一個新的 Rocket 伺服器實例
rocket::build()
// 將 "/" 路徑掛載到 index 路由,當訪問 "/" 時執行 index 函數
.mount("/", routes![index])
}
#[macro_use] extern crate rocket;
:這個語法是將 Rocket 巨集引入到作用域中,讓我們能夠使用如 #[get]
等路由註解來定義 HTTP 請求。#[get("/")]
:這是 Rocket 的路由註解,指定該函數處理 GET 請求,並定義請求的路徑(此處是根 /
路徑)。fn index()
:這個函數會處理來自 /
路徑的請求,並回傳一個靜態字串作為回應。#[launch]
:這是 Rocket 的一個屬性,用來標記應用的啟動函數。rocket::build()
:使用 Rocket 建立一個新的伺服器實例,並通過 mount
方法將路由附加到伺服器上。當我們完成 main.rs
文件編寫後,可以使用以下命令來啟動 Rocket 伺服器:
cargo run
伺服器啟動後,您應該會看到以下類似的終端輸出:
Rocket has launched from http://127.0.0.1:8000
這表示 Rocket 伺服器已經啟動並運行在本機的 http://127.0.0.1:8000
上。現在打開瀏覽器並訪問該 URL,您將看到以下訊息:
Hello from Rocket!
這就是我們在 index
函數中定義的靜態回應內容,成功顯示在瀏覽器中。
Rocket 不僅可以處理純文字回應,也能夠動態地渲染 HTML 頁面,這在需要動態生成內容或展示更複雜頁面時非常實用。Rocket 支援多種模板引擎,這裡我們將使用 Tera 模板引擎作為範例。
首先,我們需要在 Cargo.toml
文件中添加 Tera 套件:
[dependencies]
rocket = "0.5.0-rc.1"
tera = "1.17"
設定完之後,執行以下指令來更新項目:
cargo build
這樣,我們就安裝好了 Rocket 和 Tera,用於後續的模板渲染。
接著,我們需要創建一個 templates
資料夾來存放 HTML 模板文件。在專案的根目錄下創建 templates/index.html
文件,內容如下:
<!-- templates/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Rocket + Tera Example</title>
</head>
<body>
<h1>Hello from Rocket using Tera!</h1>
<p>Welcome, {{ name }}!</p>
</body>
</html>
這個模板將會顯示一個歡迎訊息,並動態展示傳遞進來的 name
參數,這個 index.html 所在的位置應該是在 templates 資料夾內。
rocket-web/
├── src/
│ └── main.rs
├── target/
├── templates/
│ └── index.html
├── .gitignore
├── Cargo.lock
└── Cargo.toml
網頁模板建立完成之後,我們接著要來設定 route 的部分。
main.rs
來渲染模板現在,我們需要修改 main.rs
來渲染這個模板,以下是修改過的程式碼:
#[macro_use]
extern crate rocket;
use rocket::response::content::RawHtml;
use rocket::State;
use tera::{Context, Tera};
#[launch]
fn rocket() -> _ {
// 初始化 Tera,將模板資料夾設為 "templates/**/*.html"
let tera = Tera::new("templates/**/*.html").unwrap();
// 構建 Rocket 伺服器,並註冊共享的 Tera 實例
rocket::build()
.manage(tera) // 將 Tera 實例註冊為共享狀態,供各處理器使用
.mount("/", routes![index]) // 將 "/" 路徑掛載到 index 路由處理器
}
#[get("/")]
fn index(tera: &State<Tera>) -> RawHtml<String> {
let mut context = Context::new(); // 創建一個 Tera Context實例為參數,用來傳遞變數到模板中
// 添加變數到 Tera 的模板上下文中
context.insert("name", "Rocket User"); // 在context實例中加入變數 "name" 的屬性,並將值設為 "Rocket User"
// 渲染模板
let rendered = tera.render("index.html", &context).unwrap(); // 使用 Tera 渲染 "index.html" 模板,並傳入 &context 作為參數
// 返回渲染後的 HTML,包裹在 RawHtml 回應中
RawHtml(rendered) // 返回渲染後的 HTML 結果
}
在這個範例中,我們使用了 Rocket 的 State
來管理 Tera 實例,並在每次請求時使用共享的 Tera 實例來渲染模板。我們將 index.html
模板中的變數 name
動態設為 "Rocket User"
,並通過 RawHtml
將渲染後的 HTML 內容返回給用戶。
執行以下命令來啟動伺服器:
cargo run
接著,訪問 http://localhost:8000/
,你會看到以下畫面:
Hello from Rocket using Tera!
Welcome, Rocket User!
實際畫面如下:
這表示我們已經成功將模板與 Rocket 結合,並能夠動態生成 HTML 頁面。
一般情況下, web service 除了要接收 GET request,還要能夠方便地處理 POST request,接收表單數據就是一個常見的例子。讓我們來看看如何使用 Rocket 處理一個簡單的表單提交。
首先,我們需要編寫一個模板來顯示表單提交後的數據。請在 templates
資料夾中創建一個名為 form-example.html
的模板文件,內容如下:
<!-- templates/form-example.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Form Submission</title>
</head>
<body>
<h1>Form Submission Result</h1>
<p>Hello, {{ name }}! You are {{ age }} years old.</p>
</body>
</html>
此模板將動態顯示提交的 name
和 age
數據。
main.rs
來渲染模板並處理表單提交接著,我們需要修改 main.rs
來處理表單提交並渲染 form-example.html
模板。這裡我們使用 Tera 來渲染表單結果。
#[macro_use]
extern crate rocket;
use rocket::form::Form;
use rocket::response::content::RawHtml;
use rocket::State;
use tera::{Context, Tera};
// 定義表單結構體
#[derive(FromForm)]
struct User {
name: String, // 使用者名稱
age: u8, // 使用者年齡
}
#[launch]
fn rocket() -> _ {
// 初始化 Tera 模板引擎
let tera = Tera::new("templates/**/*.html").unwrap();
// 構建 Rocket 伺服器並註冊路由
rocket::build()
.manage(tera) // 將 Tera 實例設為共享狀態
.mount("/", routes![index, submit]) // 掛載路由
}
// 定義一個處理 GET 請求的處理器,顯示表單
#[get("/")]
fn index() -> RawHtml<&'static str> {
// 簡單的表單 HTML,提供表單輸入
RawHtml(r#"
<form action="/submit" method="post">
<label for="name">Name:</label>
<input type="text" id="name" name="name"><br><br>
<label for="age">Age:</label>
<input type="number" id="age" name="age"><br><br>
<input type="submit" value="Submit">
</form>
"#)
}
// 定義一個處理 POST 請求的處理器,處理表單數據並渲染結果模板
#[post("/submit", data = "<user_form>")]
fn submit(user_form: Form<User>, tera: &State<Tera>) -> RawHtml<String> {
let mut context = Context::new(); // 創建 Tera 上下文
context.insert("name", &user_form.name); // 插入變數 "name"
context.insert("age", &user_form.age.to_string()); // 插入變數 "age"
// 渲染模板,將提交的數據顯示在表單結果頁面
let rendered = tera.render("form-example.html", &context).unwrap();
RawHtml(rendered) // 返回渲染後的 HTML
}
http://localhost:8000/
,將會看到以下表單頁面:<form action="/submit" method="post">
<label for="name">Name:</label>
<input type="text" id="name" name="name"><br><br>
<label for="age">Age:</label>
<input type="number" id="age" name="age"><br><br>
<input type="submit" value="Submit">
</form>
form-example.html
中。假設輸入 name = "Alice"
且 age = 30
,則結果將會是:Hello, Alice! You are 30 years old.
實際畫面如下:
這樣,我們成功地利用 Rocket 和 Tera 模板引擎來渲染表單提交結果。
我們已經學會如何使用 Rocket 建立一個簡單的 Web 伺服器,現在來對比 Rocket 和 Actix 這兩個框架的差異。
Actix 是一個完全基於非同步的框架,這讓它在高併發情境下特別有優勢。而 Rocket 採用了同步模式,雖然目前的 Rocket 0.5 支援非同步函數,但其核心理念依然傾向於同步編程。這意味著:
Rocket 擅長在編譯時進行靜態檢查,能夠在編譯時檢測大部分錯誤,尤其是路由參數和型別檢查。因此,Rocket 的開發過程中會有更少的運行時錯誤。
Actix 則更依賴運行時的錯誤處理,開發者在設計 API 時需要更加注意對異常情況的處理。
從性能角度來看,Actix 通常在高併發情境下表現更好,它經常被用來處理需要極低延遲的應用場景,例如高頻 API 請求。Rocket 在小規模應用中性能也非常優秀,但當應用需要承擔大量並發請求時,Actix 的非同步特性會讓它更具優勢。
Actix 擁有非常豐富的生態系統和插件,並且有大量的擴展功能,例如 WebSocket 支持、REST API 構建工具等。Rocket 的生態系統相對較小,但它的優點在於官方文檔非常詳盡,且框架設計精簡,開發起來更加輕量。
首先總結一下Rocket套件的操作步驟:
cargo run
啟動伺服器,通過瀏覽器訪問確認應用運行狀況。在這篇文章中,我們介紹了 Rocket 框架的基本使用方法,並對比了它與 Actix 的差異。Actix 作為高併發環境下的首選框架,以其非同步支持和 Actor 模型聞名,而 Rocket 則以簡單直觀的同步編程模式和強大的編譯時錯誤檢查功能受到許多初學者與中型應用開發者的青睞。
看得出來,Rocket 適合作為新手學習使用,當然 Rocket 框架也已經能夠提供足夠的 web service 所應該具備的多項功能,下一篇文章我們將繼續探索如何使用 Rocket 建立 RESTful API 以及與資料庫互動溝通的方法。